/*
 *    Copyright (c) 2018-2025, lengleng All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the pig4cloud.com developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: lengleng (wangiegie@gmail.com)
 */
package com.pig4cloud.pigx.strategy.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pigx.common.customize.contants.BigDecimalConstants;
import com.pig4cloud.pigx.common.customize.enums.CommonDictEnum;
import com.pig4cloud.pigx.common.customize.exception.DuckweedException;
import com.pig4cloud.pigx.common.customize.mysql.utils.MysqlUtil;
import com.pig4cloud.pigx.common.customize.redis.constants.RedisKeyEnum;
import com.pig4cloud.pigx.common.customize.redis.utils.RedisUtils;
import com.pig4cloud.pigx.common.customize.security.utils.SecurityUtil;
import com.pig4cloud.pigx.common.security.service.PigxUser;
import com.pig4cloud.pigx.strategy.dto.StrategyGroupContractAddDTO;
import com.pig4cloud.pigx.strategy.dto.StrategyGroupContractEditDTO;
import com.pig4cloud.pigx.strategy.entity.StrategyContract;
import com.pig4cloud.pigx.strategy.entity.StrategyGroupContract;
import com.pig4cloud.pigx.strategy.mapper.StrategyContractMapper;
import com.pig4cloud.pigx.strategy.mapper.StrategyGroupContractMapper;
import com.pig4cloud.pigx.strategy.mapstruct.StrategyGroupContractStruct;
import com.pig4cloud.pigx.strategy.service.StrategyGroupContractService;
import com.pig4cloud.pigx.strategy.vo.StrategyGroupContractAppVO;
import com.pig4cloud.pigx.strategy.vo.StrategyGroupContractOptionVO;
import com.pig4cloud.pigx.system.enums.SystemProductEnum;
import lombok.RequiredArgsConstructor;
import org.apache.logging.log4j.util.Strings;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;

/**
 * 策略组-合约
 *
 * @since 2022-11-24 10:39:27
 */
@Service
@RequiredArgsConstructor
public class StrategyGroupContractServiceImpl extends ServiceImpl<StrategyGroupContractMapper, StrategyGroupContract> implements StrategyGroupContractService {

	private final StrategyContractMapper strategyContractMapper;

	private final SecurityUtil securityUtil;
	private final RedisUtils redisUtils;

	private final MysqlUtil mysqlUtil;

	/**
	 * 分页查询
	 *
	 * @param page      分页条件
	 * @param condition 查询条件
	 * @return {@link Page< StrategyGroupContract>}
	 * @since 2022/11/24 10:48
	 */
	@Override
	public Page<StrategyGroupContract> page(Page<StrategyGroupContract> page, StrategyGroupContract condition) {

		QueryWrapper<StrategyGroupContract> query = new QueryWrapper<>();
		query.lambda()
				.like(Strings.isNotBlank(condition.getName()), StrategyGroupContract::getName, condition.getName())
				.orderByDesc(StrategyGroupContract::getCreateTime);
		page = baseMapper.selectPage(page, query);

		// 遍历
		page.getRecords().forEach(i -> {
			// 翻译字典项
			i.setProductType(SystemProductEnum.Type.findName(i.getProductType()));
			i.setStatus(CommonDictEnum.Status.findName(i.getStatus()));
			i.setIsOpen(CommonDictEnum.YesOrNo.findName(i.getIsOpen()));
		});

		return page;

	}

	/**
	 * 新增
	 *
	 * @param dto 数据
	 * @since 2022/11/24 11:31
	 */
	@Override
	@Transactional(rollbackFor = Throwable.class)
	public void add(StrategyGroupContractAddDTO dto) {

		StrategyGroupContract entity = StrategyGroupContractStruct.INSTANCE.addDTO2entity(dto);
		validParam(entity);

		PigxUser user = SecurityUtil.getLoginUserNotNull();
		LocalDateTime now = LocalDateTime.now();

		entity.setCreateName(user.getName());
		entity.setUpdateName(user.getName());
		securityUtil.initCreateAbout(entity, user, now);

		if (baseMapper.add(entity) != 1) {
			String errorMsg = String.format("名称为{%s}的数据已存在", dto.getName());
			throw new DuckweedException(errorMsg);
		}

		// 刷新缓存
		redisUtils.flushPersistentCache(RedisKeyEnum.STRATEGY_GROUP_CONTRACT, entity.getId(), entity);

	}

	/**
	 * 校验参数
	 *
	 * @param entity 实体类
	 * @since 2022/11/26 17:50
	 */
	private void validParam(StrategyGroupContract entity) {

		// 判断总分红比例是否等于100%
		BigDecimal totalDividend = entity.getPlatformRatio().add(entity.getCustomerRatio());
		if (totalDividend.compareTo(BigDecimalConstants.HUNDRED) != 0) {
			throw new DuckweedException("平台分成比例与客户分成比例之和必须等于100%");
		}

	}

	/**
	 * 修改
	 *
	 * @param dto 数据
	 * @since 2022/11/24 11:31
	 */
	@Override
	@Transactional(rollbackFor = Throwable.class)
	public void edit(StrategyGroupContractEditDTO dto) {

		StrategyGroupContract entity = StrategyGroupContractStruct.INSTANCE.editDTO2entity(dto);
		validParam(entity);

		PigxUser user = SecurityUtil.getLoginUserNotNull();
		LocalDateTime now = LocalDateTime.now();

		entity.setUpdateName(user.getName());
		securityUtil.initUpdateAbout(entity, user, now);

		if (baseMapper.edit(entity) != 1) {
			String errorMsg = String.format("名称为{%s}的数据已存在", dto.getName());
			throw new DuckweedException(errorMsg);
		}

		// 刷新缓存
		entity = baseMapper.selectById(entity.getId());
		redisUtils.flushPersistentCache(RedisKeyEnum.STRATEGY_GROUP_CONTRACT, entity.getId(), entity);

	}

	/**
	 * 根据id逻辑删除
	 *
	 * @param id id
	 * @since 2022/11/24 11:31
	 */
	@Override
	@Transactional(rollbackFor = Throwable.class)
	public void del(Long id) {

		PigxUser user = SecurityUtil.getLoginUserNotNull();
		LocalDateTime now = LocalDateTime.now();

		// 检查引用
		checkReferences(id);

		UpdateWrapper<StrategyGroupContract> update = new UpdateWrapper<>();
		update.lambda()
				.set(StrategyGroupContract::getUpdateBy, user.getId())
				.set(StrategyGroupContract::getUpdateName, user.getName())
				.set(StrategyGroupContract::getUpdateTime, now)
				.set(StrategyGroupContract::getIsDeleted, null)
				.eq(StrategyGroupContract::getId, id);
		if (baseMapper.update(null, update) != 1) {
			throw new DuckweedException("操作失败");
		}

		// 刷新缓存
		redisUtils.delPersistentCache(RedisKeyEnum.STRATEGY_GROUP_CONTRACT, id);

	}

	/**
	 * 检查引用
	 *
	 * @param id id
	 * @since 2022/11/24 16:04
	 */
	private void checkReferences(Long id) {

		// 检查在策略-合约表strategy_contract中的引用
		QueryWrapper<StrategyContract> queryStrategy = new QueryWrapper<>();
		queryStrategy.lambda()
				.eq(StrategyContract::getGroupId, id);
		Long strategyCount = strategyContractMapper.selectCount(queryStrategy);
		if (strategyCount > 0) {
			throw new DuckweedException("该策略组数据正在被某个策略使用");
		}

	}

	/**
	 * 查询选项数据
	 *
	 * @return {@link List< StrategyGroupContractOptionVO>}
	 * @since 2022/11/24 14:26
	 */
	@Override
	public List<StrategyGroupContractOptionVO> option() {

		QueryWrapper<StrategyGroupContract> query = new QueryWrapper<>();
		query.lambda()
				.select(StrategyGroupContract::getId,
						StrategyGroupContract::getName)
				.eq(StrategyGroupContract::getStatus, CommonDictEnum.Status.NORMAL.getValue())
				.orderByAsc(StrategyGroupContract::getName);
		List<StrategyGroupContract> list = baseMapper.selectList(query);

		return StrategyGroupContractStruct.INSTANCE.list2OptionVOList(list);

	}

	/**
	 * 禁用策略组
	 *
	 * @param id id
	 * @since 2022/11/26 17:57
	 */
	@Override
	@Transactional(rollbackFor = Throwable.class)
	public void disable(Long id) {

		PigxUser user = SecurityUtil.getLoginUserNotNull();
		LocalDateTime now = LocalDateTime.now();

		UpdateWrapper<StrategyGroupContract> update = new UpdateWrapper<>();
		update.lambda()
				.set(StrategyGroupContract::getUpdateBy, user.getId())
				.set(StrategyGroupContract::getUpdateTime, now)
				.set(StrategyGroupContract::getUpdateName, user.getName())
				.set(StrategyGroupContract::getStatus, CommonDictEnum.Status.DEPRECATED.getValue())
				.eq(StrategyGroupContract::getId, id)
				.eq(StrategyGroupContract::getStatus, CommonDictEnum.Status.NORMAL.getValue())
				.eq(StrategyGroupContract::getIsDeleted, CommonDictEnum.IsDeleted.NO.getValue());
		if (baseMapper.update(null, update) != 1) {
			throw new DuckweedException("数据状态已发生改变");
		}

		// 刷新缓存
		StrategyGroupContract entity = baseMapper.selectById(id);
		redisUtils.flushPersistentCache(RedisKeyEnum.STRATEGY_GROUP_CONTRACT, entity.getId(), entity);

	}

	/**
	 * 启用策略组
	 *
	 * @param id id
	 * @since 2022/11/26 17:57
	 */
	@Override
	@Transactional(rollbackFor = Throwable.class)
	public void enable(Long id) {

		PigxUser user = SecurityUtil.getLoginUserNotNull();
		LocalDateTime now = LocalDateTime.now();

		UpdateWrapper<StrategyGroupContract> update = new UpdateWrapper<>();
		update.lambda()
				.set(StrategyGroupContract::getUpdateBy, user.getId())
				.set(StrategyGroupContract::getUpdateTime, now)
				.set(StrategyGroupContract::getUpdateName, user.getName())
				.set(StrategyGroupContract::getStatus, CommonDictEnum.Status.NORMAL.getValue())
				.eq(StrategyGroupContract::getId, id)
				.eq(StrategyGroupContract::getStatus, CommonDictEnum.Status.DEPRECATED.getValue())
				.eq(StrategyGroupContract::getIsDeleted, CommonDictEnum.IsDeleted.NO.getValue());
		if (baseMapper.update(null, update) != 1) {
			throw new DuckweedException("数据状态已发生改变");
		}

		// 刷新缓存
		StrategyGroupContract entity = baseMapper.selectById(id);
		redisUtils.flushPersistentCache(RedisKeyEnum.STRATEGY_GROUP_CONTRACT, entity.getId(), entity);

	}

	/**
	 * 客户端分页查询
	 *
	 * @param page      分页数据
	 * @param condition 查询条件
	 * @return {@link Page< StrategyGroupContractAppVO>}
	 * @since 2022/11/27 16:33
	 */
	@Override
	public Page<StrategyGroupContractAppVO> appPage(Page<StrategyGroupContract> page, StrategyGroupContract condition) {

		QueryWrapper<StrategyGroupContract> query = new QueryWrapper<>();
		query.lambda()
				.select(StrategyGroupContract::getId,
						StrategyGroupContract::getProductType,
						StrategyGroupContract::getName,
						StrategyGroupContract::getProfile,
						StrategyGroupContract::getIsOpen
						)
				.eq(StrategyGroupContract::getStatus, CommonDictEnum.Status.NORMAL.getValue())
				.orderByAsc(StrategyGroupContract::getBalanceLimit);
		page = baseMapper.selectPage(page, query);
		List<StrategyGroupContract> list = page.getRecords();
		if (CollectionUtils.isEmpty(list)) {
			return MysqlUtil.EMPTY_PAGE;
		}

		List<StrategyGroupContractAppVO> voList = StrategyGroupContractStruct.INSTANCE.list2AppVOList(list);

		// 遍历
		voList.forEach(i -> {
			// 翻译字典项
			i.setProductType(SystemProductEnum.Type.findName(i.getProductType()));
			i.setIsOpen(CommonDictEnum.YesOrNo.findName(i.getIsOpen()));
		});

		return MysqlUtil.transferPage(page, voList);

	}

	/**
	 * 初始化redis缓存
	 */
	@Override
	public void initRedis() {
		try {
			mysqlUtil.startLocalTx();
			List<StrategyGroupContract> list = new StrategyGroupContract().selectList(new QueryWrapper<StrategyGroupContract>(){{
				last("for update");
			}});
			for (StrategyGroupContract contract : list) {
				redisUtils.flushPersistentCache(RedisKeyEnum.STRATEGY_GROUP_CONTRACT, contract.getId(), contract);
			}
			mysqlUtil.commitLocalTx();
		} catch (Exception e) {
			mysqlUtil.rollbackLocalTx();
		}
	}
}
